I recently had the idea about a switch case statement for use in LaTeX
packages. I needed to check user input (a string) and execute
different macros on different inputs. of a list. Because I could not
find a switch-case statement I implemented it using code like this:
\def\@tempa{foo}
\ifx\@tempa\user@input
...
\else
\def\@tempa{bar}
\ifx\@tempa\user@input
...
\fi\fi
Now I found the time to code a switch-case statement in plainTeX:
My idea about the syntax is:
\switch{<input value>}
\case{<case 1 value>}{<case 1 code}
\case{<case 2 value>}{<case 2 code}
...
\case{<case n value>}{<case n code}
\default{<default code>}
\endswitch
Instead of \endswitch there could also be \hctiws (switch reversed
like \fi for \if).
I also implemented versions of \switch and \case which expand there
argument: \eswitch, \ecase ('e' like 'edef'), or
check the definition instead the content: \switchx, \casex ('x' like
'ifx'). The different \switch and \case macros can be mixed.
The switch-statement stores the code in a macro similar to macros like
\@ifnextchar and uses a TeX group to localise its internal macros. The
code of the selected switch is then executed after the end of the
group. This makes the switch cascade-able and allows the case code to
include macros which read the tokens after the \endswitch.
Here my code. It is not yet fully tested. Comments are very welcome. I
might provide this as a package. Any suggestion about a name? Maybe
'switch' or 'swcase'. 'switchcase' might be too long.
%%%%%%%%%%% switch.tex %%%%%%%%%%%%%
\catcode`\@=\catcode`\A
% Normal (no expansion):
\def\switch#1{%
\begingroup
\let\switch@exec\empty
\def\switch@want{#1}%
\ignorespaces
}
\def\case#1#2{%
\def\switch@might{#1}%
\ifx\switch@might\switch@want
\def\switch@exec{#2}%
\expandafter\switch@skiprest
\fi
\ignorespaces
}
% Expansion of the argument ('e' as in 'edef'):
\def\eswitch#1{%
\begingroup
\let\switch@exec\empty
\edef\switch@want{#1}%
\ignorespaces
}
\def\ecase#1#2{%
\edef\switch@might{#1}%
\ifx\switch@might\switch@want
\def\switch@exec{#2}%
\expandafter\switch@skiprest
\fi
\ignorespaces
}
% Check definition not content ('x' as in 'ifx'):
\def\switchx#1{%
\begingroup
\let\switch@exec\empty
\let\switch@want#1\relax
\ignorespaces
}
\def\casex#1#2{%
\ifx\switch@want#1\relax
\def\switch@exec{#2}%
\expandafter\switch@skiprest
\fi
\ignorespaces
}
% Default case. Identical for all different switch statements.
\def\default#1{%
\def\switch@exec{#1}%
\switch@skiprest
}
\def\switch@skiprest#1\endswitch{%
\expandafter\switch@realend
\expandafter{\switch@exec}%
}
\def\switch@realend#1{%
\endgroup
#1%
}
\def\endswitch{%
}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Some Testcases:
\newlinechar=`^^J
\def\test#1{%
\switch{#1}
\case{test1}{\message{test1^^J}}
\case{test2}{\message{test2^^J}}
\case{test3}{\message{test3^^J}}
\case{test4}{\message{sub-switch 1:^^J }
\switch{abc}
\case{a}{\message{a^^J}}
\case{ab}{\message{ab^^J}}
\case{abc}{\message{abc^^J}}
\endswitch
}
\case{test5}{\message{sub-switch 2:^^J }\test{test1}}
\default{\message{default^^J}}
\endswitch
}
\message{^^J switch: ^^J}
\test{test1}
\test{test4}
\test{other}
\test{test5}
\def\testveca{test1}
\def\testvecb{test2}
\def\testvecc{test3}
\def\testvecd{test4}
\let\testwant\testvecb
\message{^^J switchx: ^^J}
\switchx{\testwant}
\casex{\testveca}{\message{testa^^J}}
\casex{\testvecb}{\message{testb^^J}}
\casex{\testvecc}{\message{testc^^J}}
\casex{\testvecd}{\message{testd^^J}}
\default{\message{default2^^J}}
\endswitch
\def\testwant{\testvecc}
\message{^^J eswitch: ^^J}
\eswitch{\testwant}
\ecase{\testveca}{\message{testa^^J}}
\ecase{\testvecb}{\message{testb^^J}}
\ecase{\testvecc}{\message{testc^^J}}
\ecase{\testvecd}{\message{testd^^J}}
\default{\message{default2^^J}}
\endswitch
% End TeX of LaTeX run
\csname bye\endcsname
\end{document}
%%%%%%%%%%% END OF CODE %%%%%%%%%%%%
Some observations:
The different switch commands are necessary, but the
user need not have to remember different \case commands:
\def\initswitch{% shorthand
\begingroup
\let\switch@exec\empty
}
\def\switch#1{%
\initswitch
\def\switch@want{#2}% or \edef for \eswitch
\let\case\normalcase% or \expandingcase
\ignorespaces
}
\def\switchx#1{
\initswitch
\let\switch@want#2%
\let\case\meaningcase
\ignorespaces
}
Except for the first line, all the case commands are
nearly the same, so code could be shared.
\def\normalcase#1{%
\def\switch@might{#1}% \edef for \expandingcase
\fincase
}
\def\meaningcase#1{%
\let\switch@might#1%
\fincase
}
\long\def\fincase#1{% \long in case of \par
\ifx\switch@might\switch@want
\def\switch@exec{#1}%
\expandafter\switch@skiprest
\fi
\ignorespaces
}
>\def\switch@skiprest#1\endswitch{%
> \expandafter\switch@realend
> \expandafter{\switch@exec}%
>}
>
>\def\switch@realend#1{%
> \endgroup
> #1%
>}
Or
\def\switch@skiprest#1\endswitch{%
\expandafter\endgroup\switch@exec
}
Dan
To reply by email, change LookInSig to luecking
> Or
> \def\switch@skiprest#1\endswitch{%
> \expandafter\endgroup\switch@exec
> }
Thanks for that suggestion. I just learned something.
Best,
Martin
In general, this is done with \csname
\csname IDENT:<sanitized_user_text>\endcsname
where the IDENT string is different from regular macros and
for different switch uses.
You would define
\@namedef{IDENT:<case 1 text>}{<case 1 code>}
\@namedef{IDENT:<case 2 text>}{<case 2 code>}
Your macros look good though..
Donald Arseneau as...@triumf.ca
> Your macros look good though..
Thanks.
Martin
I wrote an article about basic control sequences (including \switch and
\CASE) in the 1990s.
http://www.tug.org/TUGboat/Articles/tb13-1/tb34fine.pdf
http://www.tug.org/TUGboat/Articles/tb14-1/tb38fine.pdf
Please take a look at them.
--
Jonathan
I just saw a package 'control.sty' mentioned in one of this
publications, but I can't find it on CTAN.
Thanks,
Martin
>> I wrote an article about basic control sequences (including \switch and
>> \CASE) in the 1990s.
>> http://www.tug.org/TUGboat/Articles/tb13-1/tb34fine.pdf
>> http://www.tug.org/TUGboat/Articles/tb14-1/tb38fine.pdf
>>
>> Please take a look at them.
> Thank you so much for the links. I will have a deeper look at them on
> the weekend.
>
> I just saw a package 'control.sty' mentioned in one of this
> publications, but I can't find it on CTAN.
Apologies. I don't think it ever got to onto CTAN. I've probably got a
copy at home, somewhere.
There's nothing in that file that's not in the articles.
--
Jonathan
> {IDENT:<sanit. user input> should be expandable, while my switch
> statement is not.
It depends on how much sanitization is needed, which can be a problem.
I forget already, is \scantokens expandable? If so, it would be much
better for this than the old \def...\meaning.
Donald Arseneau
You don't need sanitization in all cases. \csname expands the macros
which allows the user to provide it's input as macro which is normally
what you want.
If a unexpandable macro is provided \csname will fail (at least I
think so) and the user will become the appropriate error message. Then
it is his/her problem.
Martin
Ah right, so still no better than \meaning.
> If a unexpandable macro is provided \csname will fail (at least I
> think so) and the user will become the appropriate error message. Then
> it is his/her problem.
Yes, it was sanitization of such things that I was thinking about.
Your code using \def and \ifx works fine (I expect) with such
strings whereas unsanitized \csname will fail.
Donald
> Hi,
>
> I recently had the idea about a switch case statement for use in LaTeX
> packages. I needed to check user input (a string) and execute
> different macros on different inputs. of a list. Because I could not
> find a switch-case statement I implemented it using code like this:
> \def\@tempa{foo}
> \ifx\@tempa\user@input
> ...
> \else
> \def\@tempa{bar}
> \ifx\@tempa\user@input
> ...
> \fi\fi
>
> Now I found the time to code a switch-case statement in plainTeX:
>
> My idea about the syntax is:
>
> \switch{<input value>}
> \case{<case 1 value>}{<case 1 code}
> \case{<case 2 value>}{<case 2 code}
> ...
> \case{<case n value>}{<case n code}
> \default{<default code>}
> \endswitch
I the expl3 code we have case switches that look similar to this. From the
documentation (in l3prg.dtx) for the function called \prg_case_int:nnn we
have
% This function evaluates the first \meta{integer expr} and then compares
it
% to the values found in the list. Thus the expression
% \begin{verbatim}
% \prg_case_int:nnn{2*5}{
% {5}{Small} {4+6}{Medium} {-2*10}{Negative}
% }{Other}
% \end{verbatim}
% evaluates first the term to look for and then tries to find this
% value in the list of values. If the value is found, the code on its
% right is executed after removing the remainder of the list. If the
% value is not found, the \meta{else case} is executed. The example
% above will return ``Medium''.
Others exist and the code is fairly simple. You might be able to find
inspiration in the implementation or come up with suggestions for
improvements.
--
Morten